use axum::{
    routing::{get, post},
    Router,
};

use serde::Deserialize;

use crate::{
    error::AppErr,
    serialport::{self, close_port, open_port, ping, port_is_open, write_port},
    serve::utils::{Cbor, Void},
};

use super::{cbor_ok, CborRet};

async fn serial_port_info() -> CborRet<Box<[String]>> {
    let ps = serialport::available_ports()?;
    cbor_ok(ps)
}

async fn mac_address() -> CborRet<String> {
    let ret = mac_address2::mac_address_by_name("eth0")?;
    if let Some(mac) = ret {
        let v = mac.to_string();
        return cbor_ok(v);
    }
    Err(AppErr::Static("not found eth0"))
}

#[derive(Debug, Deserialize)]
struct OpenArgs {
    name: String,
    baud_rate: u32,
}

async fn open_serial_port(args: Cbor<OpenArgs>) -> Result<Void, AppErr> {
    open_port(&args.name, args.baud_rate).await?;
    Ok(Void)
}

async fn close_serial_port() -> Void {
    close_port().await;
    Void
}

async fn serial_port_is_open() -> Cbor<bool> {
    Cbor(port_is_open().await)
}

#[derive(Debug, Deserialize)]
struct WriteArgs {
    #[serde(with = "serde_bytes")]
    data: Box<[u8]>,
}

async fn serial_port_write(args: Cbor<WriteArgs>) -> Result<Void, AppErr> {
    write_port(&args.data).await?;
    Ok(Void)
}

async fn on_ping(dst: Cbor<u8>) -> Result<Void, AppErr> {
    ping(dst.0).await?;
    Ok(Void)
}

pub fn register() -> Router {
    Router::new()
        .route("/available_ports", get(serial_port_info))
        .route("/mac_address", get(mac_address))
        .route("/open_port", post(open_serial_port))
        .route("/close_port", post(close_serial_port))
        .route("/port_is_open", get(serial_port_is_open))
        .route("/write_port", post(serial_port_write))
        .route("/ping", post(on_ping))
}
